/*
 * ImagesMng.cpp
 *
 *  Created on: 28.01.2010
 *      Author: Admin
 */

/*
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fts.h>
*/
#include "ImagesMng.h"

/**************************** Public methods *********************************/

ImagesMng::ImagesMng()
{

	_current_index = -1;
	_prev_img = NULL;
	_current_img = NULL;
	_next_img = NULL;
}

ImagesMng::~ImagesMng()
{
	ClearBuffers();
	_files.clear();
	_dir.clear();
}

void ImagesMng::Open(std::string dir, std::string filename)
{
	if (dir[dir.size() - 1] != '/')
		dir += "/";

/*
	if (IsSupportedArchive(filename))
		dir = UnpackArchiveToDir(dir, filename);

	const char *dirs[] = { dir.c_str(), NULL };
	FTS *tree = fts_open((char * const *)dirs,
	                     FTS_PHYSICAL | FTS_NOCHDIR | FTS_NOSTAT, NULL);
	if (tree)
*/
	DIR *d = iv_opendir(dir.c_str());
	if (d != 0)

	{
		ClearBuffers();
		_files.clear();
		_dir = dir;
		_current_index = -1;

/*
		FTSENT *node;
*/
		struct dirent *ep;

		std::string name;
		int i = 0;

/*
		while ((node = fts_read(tree)))
*/
		while (ep = iv_readdir(d))
/*
		{
			if (node->fts_info & FTS_F &&
			    (HasSuffix(node->fts_path, "bmp") ||
			     HasSuffix(node->fts_path, "jpeg") ||
			     HasSuffix(node->fts_path, "jpg") ||
			     HasSuffix(node->fts_path, "png"))) {
				name = node->fts_path + _dir.size();
*/
		{
			std::string ext = GetFileExtension(ep->d_name);
			if (ext == "bmp" || ext == "jpeg" || ext == "jpg" || ext == "png")
			{
				name = ep->d_name;


				if (!filename.empty() && filename == name)
				{
					_current_index = i;
				}
				_files.push_back(name);
				i++;
			}
		}

/*
		fts_close(tree);
*/
		iv_closedir(d);

		if (!_files.empty())
		{
//			TODO: Слабое место. Подумать о загрузке конкретной картинки
//			if (!filename.empty())
//				name = _dir + filename;
//			else
//				name = _dir + _files[_current_index];

/*
			sort(_files.begin(), _files.end());
*/

			if(_current_index == -1)
				_current_index = 0;
			name = _dir + _files[_current_index];
			_current_img = OpenFile(name);
		}
		else
		{
			//_current_index = -1;
			Log::msg("В данном каталоге нет файлов для отображения");
		}
	}
	else
		//Message(ICON_ERROR, "Error", "Не удалось открыть выбранный каталог", 2000);
		Log::msg("Не удалось открыть выбранный каталог");
}

void ImagesMng::Open(std::string fullname)
{
	size_t slash_pos = fullname.find_last_of("/");
	if (slash_pos != std::string::npos)
		Open(fullname.substr(0, slash_pos), fullname.substr(slash_pos + 1));
}

ibitmap* ImagesMng::Prev()
{
	if (HasPrev())
	{
		if (_next_img != NULL)
			delete _next_img;
		_next_img = _current_img;
		if (_prev_img != NULL)
		{
			_current_img = _prev_img;
			_prev_img = NULL;
			_current_index--;
		}
		else
		{
			_current_img = GetImage(--_current_index);
		}
		//		return Current();
	}
	return Current();
}

ibitmap* ImagesMng::Next()
{
	if (HasNext())
	{
		if (_prev_img != NULL)
			delete _prev_img;
		_prev_img = _current_img;
		if (_next_img != NULL)
		{
			_current_img = _next_img;
			_next_img = NULL;
			_current_index++;
		}
		else
		{
			_current_img = GetImage(++_current_index);
		}
		//		return Current();
	}
	return Current();
}

ibitmap* ImagesMng::GoTo(int index)
{
	if (index >= 0 && index < (int)_files.size())
	{
		if (index == _current_index)
		{
			return Current();
		}
		if (index == _current_index - 1)
		{
			return Prev();
		}
		if (index == _current_index + 1)
		{
			return Next();
		}
		ClearBuffers();
		_current_index = index;
		_current_img = GetImage(_current_index);
		//		return Current();
	}
	return Current();
}

bool ImagesMng::IsBuffered()
{
	if (_prev_img == NULL && HasPrev())
		return false;
	if (_next_img == NULL && HasNext())
		return false;
	return true;
}

void ImagesMng::Buffering()
{
	if (_next_img == NULL && HasNext())
		_next_img = GetImage(_current_index + 1);
	if (_prev_img == NULL && HasPrev())
		_prev_img = GetImage(_current_index - 1);
}

std::string ImagesMng::GetFileName(size_t index)
{
	if (index >= 0 && index < _files.size())
	{
		return _files[index];
	}
	std::string empty;
	return empty;
}

std::string ImagesMng::GetFileFullame(size_t index)
{
	if (index >= 0 && index < _files.size())
	{
		return _dir + _files[index];
	}
	std::string empty;
	return empty;
}

ibitmap* ImagesMng::GetImage(size_t index)
{
	if (index >= 0 && index < _files.size())
	{
		std::string filename = _dir + _files[index];
		return OpenFile(filename);
	}
	return NULL;
}

/*
bool ImagesMng::IsSupportedArchive(std::string path) {
	return GetArchiveType(path) != UNSUPPORTED;
}

int ImagesMng::DeleteUnpackedArchive() {
	std::string cmd = "rm -rf \"" + _dir + "\"";
	int ret = system(cmd.c_str());
	if (ret && WIFEXITED(ret))
		ret = WEXITSTATUS(ret);
	return ret;
}
*/

/**************************** Private methods ********************************/

ibitmap* ImagesMng::OpenPNG(char *filename)
{
	png_byte header[8]; // 8 is the maximum size that can be checked

	/* open file and test for it being a png */
	FILE *fp = fopen(filename, "rb");
	if (!fp)
	{
		Log::msg("[read_png_file] File %s could not be opened for reading", filename);
		return NULL;
	}
	fread(header, 1, 8, fp);
	if (png_sig_cmp(header, 0, 8))
	{
		Log::msg("[read_png_file] File %s is not recognized as a PNG file", filename);
		return NULL;
	}

	/* initialize stuff */
	png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);

	if (!png_ptr)
	{
		Log::msg("[read_png_file] png_create_read_struct failed");
		return NULL;
	}

	png_infop info_ptr = png_create_info_struct(png_ptr);
	if (!info_ptr)
	{
		png_destroy_read_struct(&png_ptr, NULL, NULL);
		Log::msg("[read_png_file] png_create_info_struct failed");
		return NULL;
	}

	if (setjmp(png_jmpbuf(png_ptr)))
	{
		png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
		Log::msg("[read_png_file] Error during init_io");
		return NULL;
	}

	png_init_io(png_ptr, fp);
	png_set_sig_bytes(png_ptr, 8);

	// changed by zola so we don't need to have public FILE pointers
	//	png_set_read_fn(png_ptr, file, user_read_data_fcn);

	png_read_info(png_ptr, info_ptr); // Read the info section of the png file

	// Convert palette color to true color
	if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
		png_set_palette_to_rgb(png_ptr);

	// Convert low bit colors to 8 bit colors
	if (png_ptr->buffer_size < 8)
	{
		if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY || png_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
			png_set_gray_1_2_4_to_8(png_ptr);
		else
			png_set_packing(png_ptr);
	}

	// Convert high bit colors to 8 bit colors
	if (png_ptr->buffer_size == 16)
		png_set_strip_16(png_ptr);

	if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
	{
		png_set_tRNS_to_alpha(png_ptr);
		png_set_strip_alpha(png_ptr);
	}

	if (png_ptr->color_type == PNG_COLOR_TYPE_RGBA || png_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
		png_set_strip_alpha(png_ptr);

#if 1
	// Convert true color to gray color
	if (png_ptr->color_type == PNG_COLOR_TYPE_RGBA || png_ptr->color_type == PNG_COLOR_TYPE_RGB)
		png_set_rgb_to_gray(png_ptr, 1, 1, 1);
#else
	// Convert gray color to true color
	if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY || png_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
	png_set_gray_to_rgb(png_ptr);
#endif

	png_set_interlace_handling(png_ptr);
	png_read_update_info(png_ptr, info_ptr);

	/* read file */
	if (setjmp(png_jmpbuf(png_ptr)))
	{
		png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
		Log::msg("[read_png_file] Error during read_image");
		return NULL;
	}

	int scanline = sizeof(unsigned char) * info_ptr->width * info_ptr->channels * info_ptr->bit_depth / 8;

	ibitmap* bit = (ibitmap*) malloc(sizeof(ibitmap*) + scanline * info_ptr->height);
	bit->width = info_ptr->width;
	bit->height = info_ptr->height;
	bit->depth = info_ptr->bit_depth * info_ptr->channels;
	bit->scanline = scanline;

	png_bytep * row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * info_ptr->height);
	for (unsigned int y = 0; y < info_ptr->height; y++)
		row_pointers[y] = &(bit->data[y * bit->scanline]);

	png_read_image(png_ptr, row_pointers);

	png_read_end(png_ptr, NULL);
	delete[] row_pointers;
	png_destroy_read_struct(&png_ptr, &info_ptr, 0); // Clean up memory
	fclose(fp);

	return bit;
}

ibitmap* ImagesMng::OpenJPEG(char *filename)
{
#if 1
	struct jpeg_decompress_struct cinfo;
	FILE *pFile;
	if ((pFile = fopen(filename, "rb")) == NULL)
	{
		Log::msg("[read_jpeg_file] Error opening file: %s", filename);
		return NULL;
	}
	jpeg_error_mgr jerr;
	cinfo.err = jpeg_std_error(&jerr);
	jpeg_create_decompress(&cinfo);
	jpeg_stdio_src(&cinfo, pFile);

	// Decode

	jpeg_read_header(&cinfo, TRUE);
	jpeg_start_decompress(&cinfo);

	int scanline = sizeof(unsigned char) * (&cinfo)->image_width * (&cinfo)->num_components;

	ibitmap* bit = (ibitmap*) malloc(sizeof(ibitmap*) + scanline * (&cinfo)->image_height);
	bit->width = (&cinfo)->image_width;
	bit->height = (&cinfo)->image_height;
	bit->depth = (&cinfo)->num_components * 8;
	bit->scanline = scanline;

	unsigned char** rowPtr = new unsigned char*[(&cinfo)->image_height];
	for (unsigned int i = 0; i < (&cinfo)->image_height; i++)
		rowPtr[i] = &(bit->data[i * scanline]);

	int rowsRead = 0;
	while ((&cinfo)->output_scanline < (&cinfo)->output_height)
	{
		rowsRead += jpeg_read_scanlines(&cinfo, &rowPtr[rowsRead], (&cinfo)->output_height - rowsRead);
	}

	delete[] rowPtr;
	jpeg_finish_decompress(&cinfo);

	// End decode

	jpeg_destroy_decompress(&cinfo);
	fclose(pFile);

	return bit;
#else
	return LoadJPEG(filename,1000,1000,100,100,1);
#endif
}

ibitmap* ImagesMng::OpenBMP(char *filename)
{
	return LoadBitmap(filename);
}

ibitmap* ImagesMng::OpenFile(std::string filename)
{
	std::string ext = GetFileExtension(filename);

/*
	const char *c_ext = ext.c_str();
	if (HasSuffix(c_ext, "bmp"))
*/
	if (ext == "bmp")

	{
		return OpenBMP((char*) filename.c_str());
	}

/*
	else if (HasSuffix(c_ext, "jpg") || HasSuffix(c_ext, "jpeg"))
*/
	else if (ext == "jpg" || ext == "jpeg")

	{
		return OpenJPEG((char*) filename.c_str());
	}

/*
	else if (HasSuffix(c_ext, "png"))
*/
	else if (ext == "png")

	{
		return OpenPNG((char*) filename.c_str());
	}
	return NULL;
}

std::string ImagesMng::GetFileExtension(std::string path)
{
	size_t dot_pos = path.find_last_of(".");
	if (dot_pos != std::string::npos)
		return path.substr(dot_pos + 1);
	std::string empty;
	return empty;
}

void ImagesMng::ClearBuffers()
{
	if (_prev_img != NULL)
	{
		delete _prev_img;
		_prev_img = NULL;
	}
	if (_current_img != NULL)
	{
		delete _current_img;
		_current_img = NULL;
	}
	if (_next_img != NULL)
	{
		delete _next_img;
		_next_img = NULL;
	}
}

/*
bool ImagesMng::HasSuffix(const char* path, const char *suf) {
	return (strcasecmp(path+strlen(path)-strlen(suf), suf) == 0);
}

int ImagesMng::GetArchiveType(std::string path) {
	int type;

	if (path.empty())
		return UNSUPPORTED;

	const char* f = path.c_str();
	if (HasSuffix(f, ".zip") || HasSuffix(f, ".cbz"))
		type = ZIP;
	else if (HasSuffix(f, ".rar") || HasSuffix(f, ".cbr"))
		type = RAR;
	else if (HasSuffix(f, ".tar.gz") || HasSuffix(f, ".tgz"))
		type = TGZ;
	else if (HasSuffix(f, ".tar.bz2") || HasSuffix(f, ".tbz2"))
		type = TBZ2;
	else if (HasSuffix(f, ".tar"))
		type = TAR;
	else
		type = UNSUPPORTED;

	return type;
}

std::string ImagesMng::UnpackArchiveToDir(std::string dir, std::string filename) {
	int ret, type;
	std::string error;
	std::string newdir = dir + filename + ".dir";
	if ((ret = mkdir(newdir.c_str(), 0755)) && (errno != EEXIST)) {
		error = GetLangText("@Cant_create_dir");
		error += ": " + newdir;
		Message(ICON_ERROR, GetLangText("@Program_name"), (char*)error.c_str(), 3000);
		CloseApp();
	}

	std::string cmd;
	type = GetArchiveType(filename);
	switch (type) {
		case ZIP:
			cmd = unzip_path+" -q -u \""+dir+filename+"\" -d \""+newdir+"\"";
			break;
		case RAR:
			cmd = unrar_path+" x -u \""+dir+filename+"\" \""+newdir+"\"";
			break;
		case TAR:
			cmd = "tar -C \""+newdir+"\" -xf \""+dir+filename+"\"";
			break;
		case TGZ:
			cmd = "tar -C \""+newdir+"\" -zxf \""+dir+filename+"\"";
			break;
		case TBZ2:
			cmd = "tar -C \""+newdir+"\" -jxf \""+dir+filename+"\"";
			break;
		case UNSUPPORTED:
		default:
			error = GetLangText("@Unk_book_type");
			error += ": "+filename;
			Message(ICON_ERROR, GetLangText("@Program_name"), (char*)error.c_str(), 3000);
			break;
	}

	if (cmd.empty() || (ret = system(cmd.c_str()))) {
		if (WIFEXITED(ret))
			ret = WEXITSTATUS(ret);
		if (type != ZIP || ret != 1) {
			if (cmd.size()) {
				error = GetLangText("@Unpack_fail");
				error += ": "+cmd;
				Message(ICON_ERROR, GetLangText("@Program_name"), (char*)error.c_str(), 10000);
			}
			cmd = "rm -rf \""+newdir+"\"";
			system(cmd.c_str());
			CloseApp();
		}
	}

	return newdir+"/";
}
*/